////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2009. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package ru.goodcore.managers
{

import flash.display.InteractiveObject;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.KeyboardEvent;
import flash.events.TextEvent;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.ui.KeyLocation;

import ru.goodcore.errors.GenericError;
import ru.goodcore.errors.GenericErrorCode;

//-------------------------------------
//  Events
//-------------------------------------

/**
 * Dispatched when a key is down
 *
 * @eventType			flash.events.KeyboardEvent.KEY_DOWN
 */
[Event(name="keyDown", type="flash.events.KeyboardEvent")]

/**
 * Dispatched when a key is up
 *
 * @eventType			flash.events.KeyboardEvent.KEY_UP
 */
[Event(name="keyUp", type="flash.events.KeyboardEvent")]

/**
 * KeyboardManager
 *
 * @author					BlooDHounD, Dimarik
 * @version					1.0
 * @playerversion			Flash 9
 * @langversion				3.0
 *
 * @keyword					keyboardmanager, keyboard, manager, key
 */
public class KeyboardManager extends EventDispatcher
{

	include "../../../includes/core/Version.as";

	//-------------------------------------------------------------------------
	//
	//  Class methods
	//
	//-------------------------------------------------------------------------

	/**
	 * Converts keyboard event parameters to unique key event code.
	 *
	 * @param	type				event type.
	 * @param	keyCode				key code.
	 * @param	keyLocation			key location.
	 * @param	ctrlKey				is Ctrl used
	 * @param	altKey				is Alt used
	 * @param	shiftKey			is Shift used
	 *
	 * @return						unique event code
	 *
	 * @keyword						keyboardmanager.getkeyeventcode, getkeyeventcode
	 */
	private static function getKeyEventCode(
					type:String, keyCode:uint,
					keyLocation:uint = 0, ctrlKey:Boolean = false,
					altKey:Boolean = false, shiftKey:Boolean = false):uint
	{

		if (shiftKey)						keyCode |= 1 << 8;
		if (altKey)							keyCode |= 1 << 9;
		if (ctrlKey)						keyCode |= 1 << 10;
		switch (type) {
			case KeyboardEvent.KEY_DOWN:	keyCode |= 1 << 11;	break;
		}
		switch (keyLocation) {
			case KeyLocation.NUM_PAD:		keyCode |= 1 << 12;	break;
			case KeyLocation.LEFT:			keyCode |= 1 << 13;	break;
			case KeyLocation.RIGHT:			keyCode |= 1 << 14;	break;
		}
		return keyCode;
	}

	//-------------------------------------------------------------------------
	//
	//  Constructor
	//
	//-------------------------------------------------------------------------

	/**
	 * Constructor.
	 *
	 * @param	target		Target keyboard's events dispatcher
	 */
	public function KeyboardManager(target:InteractiveObject) {
		super();
		target.addEventListener(KeyboardEvent.KEY_DOWN, this.$dispatchEvent, false, int.MAX_VALUE);
		target.addEventListener(KeyboardEvent.KEY_UP,   this.$dispatchEvent, false, int.MAX_VALUE);
	}

	//-------------------------------------------------------------------------
	//
	//  Properties
	//
	//-------------------------------------------------------------------------

	//-------------------------------------
	//  enabled
	//-------------------------------------
	/**
	 * @private
	 */
	private var _enabled:	Boolean = true;

	public function get enabled():Boolean {
		return this._enabled;
	}

	public function set enabled(value:Boolean):void {
		if (this._enabled == value) return;
		this._enabled = value;
	}

	//-------------------------------------------------------------------------
	//
	//  Private variables
	//
	//-------------------------------------------------------------------------

	/**
	 * @private
	 * Storage for last used keycode. We decide to omit repeated firing events.
	 */
	private var _lastKeyCode:	uint = 0;

	//-------------------------------------------------------------------------
	//
	//  Overridden methods: EventDispatcher
	//
	//-------------------------------------------------------------------------

	[Deprecated(message="The method is deprecated")]
	/**
	 * @private
	 */
	public override function dispatchEvent(event:Event):Boolean {
		throw new GenericError(GenericErrorCode.CLASS_DOES_NOT_IMPLEMENT);
	}


	[Deprecated(message="The method is deprecated", replacement="ru.goodcore.managers.KeyboardManager#addKeyboardEventListener")]
	/**
	 * @private
	 */
	public override function addEventListener(
									type:String, listener:Function, 
									useCapture:Boolean = true, priority:int = 0, 
									useWeakReference:Boolean = false):void
	{
		throw new GenericError(GenericErrorCode.CLASS_DOES_NOT_IMPLEMENT);
	}


	[Deprecated(message="The method is deprecated", replacement="ru.goodcore.managers.KeyboardManager#removeKeyboardEventListener")]
	/**
	 * @private
	 */
	public override function removeEventListener(
										type:String, listener:Function,
										useCapture:Boolean = false):void
	{
		throw new GenericError(GenericErrorCode.CLASS_DOES_NOT_IMPLEMENT);
	}



	//-------------------------------------------------------------------------
	//
	//  Public methods
	//
	//-------------------------------------------------------------------------

	/**
	 * Вешает событие на нажатие комбинаций клавиш.
	 * 
	 * @param	type				event type.
	 * @param	listener			listener function.
	 * @param	useCapture			event phase.
	 * @param	priority			priority.
	 * @param	useWeakReference	use weak reference.
	 * @param	keyCode				key code.
	 * @param	keyLocation			key location.
	 * @param	ctrlKey				is Ctrl used
	 * @param	altKey				is Alt used
	 * @param	shiftKey			is Shift used
	 * 
	 * 
	 * @keyword						keyboardmanager.addkeyboardeventlistener, addkeyboardeventlistener
	 * 
	 * @see							flash.events.EventDispatcher#addEventListener()
	 */
	public function addKeyboardEventListener(
					type:String, listener:Function, useCapture:Boolean = false,
					priority:int = 0, useWeakReference:Boolean = false,
					keyCode:uint = 0, keyLocation:uint = 0, ctrlKey:Boolean = false,
					altKey:Boolean = false, shiftKey:Boolean = false):void
	{
		var eventCode:String = getKeyEventCode(type, keyCode, keyLocation, ctrlKey, altKey, shiftKey).toString();
		super.addEventListener(eventCode, listener, useCapture, priority, useWeakReference);
	}

	/**
	 * Удаляет событие на нажатие комбинаций клавиш.
	 * 
	 * @param	type				event type.
	 * @param	listener			listener function.
	 * @param	useCapture			event phase.
	 * @param	keyCode				key code.
	 * @param	keyLocation			key location.
	 * @param	ctrlKey				is Ctrl used
	 * @param	altKey				is Alt used
	 * @param	shiftKey			is Shift used
	 * 
	 * 
	 * @keyword						keyboardmanager.removekeyboardeventlistener, removekeyboardeventlistener
	 * 
	 * @see							flash.events.EventDispatcher#removeEventListener()
	 */
	public function removeKeyboardEventListener(
					type:String, listener:Function, useCapture:Boolean = false, 
					keyCode:uint = 0, keyLocation:uint = 0, ctrlKey:Boolean = false, 
					altKey:Boolean = false, shiftKey:Boolean = false):void
	{
		var eventCode:String = getKeyEventCode(type, keyCode, keyLocation, ctrlKey, altKey, shiftKey).toString();
		super.removeEventListener(eventCode, listener, useCapture);
	}

	//-------------------------------------------------------------------------
	//
	//  Private methods
	//
	//-------------------------------------------------------------------------

	/**
	 * @private
	 */
	private function $dispatchEvent(e:KeyboardEvent):Boolean {
		if ( !this._enabled) 
			return false;

		var eventCode:	uint = getKeyEventCode(e.type, e.keyCode, e.keyLocation, e.ctrlKey, e.altKey, e.shiftKey);

		// Do not dispatch same repeated events
		if (this._lastKeyCode == eventCode) 
			return true;

		this._lastKeyCode = eventCode;

/*
		// Default behavior for text input is to prevent input text appearing.
		if (
			e.type == KeyboardEvent.KEY_DOWN && 
			e.target is TextField && 
			TextField(e.target).type == TextFieldType.INPUT
		) {
			IEventDispatcher(e.target).addEventListener(TextEvent.TEXT_INPUT, this.handler_textInput, false, int.MAX_VALUE);
		}
*/
		return super.dispatchEvent( new KeyboardManagerEvent(eventCode.toString(), e.target, e.bubbles, e.cancelable, e.charCode) );
	}

	/**
	 * @private
	 */
	private function handler_textInput(event:TextEvent):void {
		IEventDispatcher(event.target).removeEventListener(event.type, arguments.callee);
		event.preventDefault();
	}

}
}

//=============================================================================
//
//  Inner definitions
//
//=============================================================================

import flash.events.KeyboardEvent;
import flash.ui.KeyLocation;

import flash.events.Event;
import flash.display.InteractiveObject;

///////////////////////////////////////////////////////////////////////////////
//
//  Helper class: KeyboardManagerEvent
//
///////////////////////////////////////////////////////////////////////////////

/**
 * @private
 * Вспомогательный класс.
 * 
 * Создаёт KeyboardEvent из числового кода.
 * type - Хук на числовой тип.
 * 
 * @author					BlooDHounD
 */
internal final class KeyboardManagerEvent extends KeyboardEvent {

	//-------------------------------------------------------------------------
	//
	//  Constructor
	//
	//-------------------------------------------------------------------------

	/**
	 * @private
	 * Constructor.
	 */
	public function KeyboardManagerEvent(
								type:String, target:Object,
								bubbles:Boolean = true, cancelable:Boolean = false, 
								charCode:uint = 0)
	{
		var eventCode:uint = parseInt(type);

		super(type, bubbles, cancelable, charCode,
			// keyCode
			eventCode & 255,
			// keyLocation
			( eventCode & 1 << 12 ? KeyLocation.NUM_PAD :
				( eventCode & 1 << 13 ? KeyLocation.LEFT :
					( eventCode & 1 << 14 ? KeyLocation.RIGHT : 
						KeyLocation.STANDARD
					)
				)
			),
			// ctrlKey
			Boolean(eventCode & 1 << 10),
			// altKey
			Boolean(eventCode & 1 << 9),
			// shiftKey
			Boolean(eventCode & 1 << 8)
		);
		// сохраняем тип для переопределения
		this._type = eventCode & 1 << 11 ? KeyboardEvent.KEY_DOWN : KeyboardEvent.KEY_UP;

		this._target = target;
	}

	//-------------------------------------------------------------------------
	//
	//  Overridden properties: Event
	//
	//-------------------------------------------------------------------------

	//---------------------------------
	//  type
	//---------------------------------
	/**
	 * @private
	 */
	private var _type:		String;

	/**
	 * @private
	 * Переопределяем тип, для того что бы в евенте мы видели настоящий тип, а не числовой код.
	 */
	public override function get type():String {
		return this._type;
	}

	//---------------------------------
	//  target
	//---------------------------------
	/**
	 * @private
	 */
	private var _target:	Object;

	/**
	 * @private
	 */
	public override function get target():Object {
		return this._target;
	}

	//-------------------------------------------------------------------------
	//
	//  Overridden methods: Event
	//
	//-------------------------------------------------------------------------

	/**
	 * @private
	 */
	public override function clone():Event {
		return new KeyboardManagerEvent(super.type, this._target, super.bubbles, super.cancelable, super.charCode);
	}

}